Spring中Bean的单例及七种创建单例的方法

您所在的位置:网站首页 springboot 创建单例 Spring中Bean的单例及七种创建单例的方法

Spring中Bean的单例及七种创建单例的方法

#Spring中Bean的单例及七种创建单例的方法| 来源: 网络整理| 查看: 265

1. 单例(singleton):只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。eg:每个国家(spring)有一个总统(bean),国家的所有人(其他调用者)共享此总统。

 

2. 多例(pototype):对这个bean每次请求都会创建一个新的bean实例,类似于new

 

3. SpringBoot采用的是单例模式、@Component注解默认实例化的对象是单例,如果想声明成多例对象,可以使用@Scope(“pototype”)、@Respository默认单例、@Service默认单例、@Controller默认单例

 

4. 单例bean与单例模式:

(1)单例模式:在一个JVM进程中仅有一个实例(一个运行的Java程序必定有一个自己独立的JVM),无论在程序的何处获取实例,始终都返回一个对象 eg:Java内置的Runtime,无论在程序的何处获取实例,Runtime.getRuntime == Runtime.getRuntime 结果为true(例如建立目录、数据库的连接都需要单线程操作),单例模式能避免实例的重复创建、避免多个实例引起程序逻辑错误、节约内存。

(2)Spring单例bean:Spring的单例bean是与其容器(ApplicationContext)相关的,所以在一个JVM进程中,若有多个Spring容器,即使是单例bean,也会创建多个实例(如果在实际应用中,对象的状态会改变,就使用多例)

 

5. Spring把bean默认设置为单例的原因:

(1)提高性能,减少了新生成实例的消耗

(2)减少了JVM的垃圾回收

(3)可以快速获取到bean(单例的获取除了第一次生成之外都是从缓存中获取的)

 

 

6. 七种创建单例的方式:

(1)饿汉式:

public class Singleton1{ private static Singleton1 singleton1 = new Singleton1(); public static Singleton1 getSingleton1(){ return singleton1; }}

singleton1作为类变量直接得到初始化,优点是在多线程环境下能够保证同步,不可能被实例化两次,但是如果singleton1在很长一段时间后才使用,意味着singleton1实例开辟的堆内存会驻留很长时间,不能延迟加载,占用内存

(2)懒汉式:

public class Singleton2{ private static Singleton2 singleton1 = null; public static Singleton2 getSingleton1(){ if(singleton1 ==null){ singleton1 = new Singleton2(); } return singleton1; }}

懒汉式,是在使用的时候才去创建,这样可以避免类在初始化时提前创建,但是如果在多线程的情况下,若一开始因为线程上下文切换的原因,导致两个线程都通过了if判断,这样就new出两个实例,无法保证唯一性。延迟加载,但在多线程环境下不安全。

(3)懒汉式+同步:

public class Singleton3 { private static Singleton3 singleton1 = null; public synchronized Singleton3 getSingleton1() { if (singleton1 == null) { singleton1 = new Singleton3(); } return singleton1; }}

这种方法通过添加同步控制,满足了懒加载和singleton1实例的唯一性,但是添加同步控制后,getSingleton1()方法是串行化的,获取时需要排队等候,效率较低。

(4)懒汉式+双重检验:

ublic class Singleton4 { private static Singleton4 singleton1 = null; public static Singleton4 getSingleton1() { if (singleton1 == null) { synchronized (Singleton4.class) { if (singleton1 == null) { singleton1 = new Singleton4(); } } } return singleton1; }}

若有两个线程通过了第一个check,进入第二个check是串行化的,只能有一个线程进入,保证了唯一性,也不会有同步控制,可能会引起空指针异常,但是可能会出现JVM排序的问题

(假设这个单例创建有一些其他的资源,例如Socket、Connection,这些资源在构造函数中也会被实例化,那样在创建单例的时候,就是要实例化自身还有Socket这些资源,那根据JVM的重排序和Happens-before原则,有可能会出现先实例化自身,再去实例化Socket这些资源,若在此时只实例化了自己的情况下,别的线程调用了这个单例中Socket这些资源的方法,而此时它们可能还没有被实例化,这样就会抛出空指针的异常)

(5)懒汉式+双重检验+volatile

Volatile关键字禁止指令重排序:

public class Singleton5 { private static volatile Singleton5 singleton1 = null; public static Singleton5 getSingleton1() { if (singleton1 == null) { synchronized (Singleton5.class) { if (singleton1 == null) { singleton1 = new Singleton5(); } } } return singleton1; }}

(6)Holder方式(静态内部类):

public class Singleton6 { private Singleton6(){ } private static class Singleton{ private static Singleton6 singleton1 = new Singleton6(); } public static Singleton6 getInstance(){ return Singleton.singleton1; }}

在Singleton6中并没有singleton1的静态成员,而是放在静态内部类Singleton中,因此Singleton6的初始化并不会实例化singleton1,只有当Singleton被主动引用的时候才会实例化。

(7)枚举法:

public class Singleton7 { private Singleton7(){ } private enum Singleton{ Singleton; private final Singleton7 singleton7; Singleton(){ singleton7 = new Singleton7(); } public Singleton7 getSingleton7() { return singleton7; } }}

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3